#!/usr/bin/env node

/**
 * @author zoulei
 * Browser Server - 整合的浏览器工具服务端
 */

import express from "express";
import cors from "cors";
import bodyParser from "body-parser";
import { WebSocketServer, WebSocket } from "ws";
import fs from "fs";
import path from "path";
import { IncomingMessage } from "http";
import { Socket } from "net";
import os from "os";
import * as net from "net";
import {
  runPerformanceAudit,
  runAccessibilityAudit,
  runSEOAudit,
  AuditCategory,
} from "./lighthouse/index.js";
import { runBestPracticesAudit } from "./lighthouse/best-practices.js";

// Types and interfaces
interface BrowserServerConfig {
  host: string;
  port: number;
  logLimit: number;
  queryLimit: number;
  showRequestHeaders: boolean;
  showResponseHeaders: boolean;
}

// Global state
let consoleLogs: any[] = [];
let networkLogs: any[] = [];
let currentUrl = "";
let currentTabId: number | null = null;
let screenshotCallbacks = new Map<string, any>();

// Default configuration
const defaultConfig: BrowserServerConfig = {
  host: "127.0.0.1",
  port: 3025,
  logLimit: 1000,
  queryLimit: 50000,
  showRequestHeaders: true,
  showResponseHeaders: true,
};

let currentSettings = { ...defaultConfig };

/**
 * 查找可用端口
 */
async function getAvailablePort(startPort: number, maxAttempts: number = 50): Promise<number> {
  let currentPort = startPort;
  let attempts = 0;

  while (attempts < maxAttempts) {
    try {
      await new Promise<void>((resolve, reject) => {
        const testServer = net.createServer();

        testServer.once("error", (err: any) => {
          if (err.code === "EADDRINUSE") {
            console.error(`Port ${currentPort} is in use, trying next port...`);
            currentPort++;
            attempts++;
            resolve();
          } else {
            reject(err);
          }
        });

        testServer.once("listening", () => {
          testServer.close(() => {
            console.error(`Found available port: ${currentPort}`);
            resolve();
          });
        });

        testServer.listen(currentPort, currentSettings.host);
      });

      return currentPort;
    } catch (error: any) {
      console.error(`Error checking port ${currentPort}:`, error);
      currentPort++;
      attempts++;
    }
  }

  throw new Error(`Could not find an available port after ${maxAttempts} attempts starting from ${startPort}`);
}

/**
 * 获取默认下载文件夹
 */
function getDefaultDownloadsFolder(): string {
  const platform = os.platform();
  const homeDir = os.homedir();

  switch (platform) {
    case "win32":
      return path.join(homeDir, "Downloads");
    case "darwin":
      return path.join(homeDir, "Downloads");
    case "linux":
      return path.join(homeDir, "Downloads");
    default:
      return path.join(homeDir, "Downloads");
  }
}

/**
 * 清除所有日志
 */
function clearAllLogs(): void {
  consoleLogs = [];
  networkLogs = [];
  console.error("All logs cleared");
}

/**
 * 浏览器连接器类
 */
export class BrowserConnector {
  private wss: WebSocketServer;
  private activeConnection: WebSocket | null = null;
  private app: express.Application;
  private server: any;
  private urlRequestCallbacks: Map<string, (url: string) => void> = new Map();

  constructor(app: express.Application, server: any) {
    this.app = app;
    this.server = server;

    // 初始化WebSocket服务器
    this.wss = new WebSocketServer({
      noServer: true,
      path: "/extension-ws",
    });

    this.setupRoutes();
    this.setupWebSocket();
  }

  /**
   * 设置路由
   */
  private setupRoutes(): void {
    // 身份验证端点
    this.app.get("/.identity", (req, res) => {
      res.json({
        signature: "mcp-browser-connector-24x7",
        version: "1.2.0",
        status: "running"
      });
    });

    // 控制台日志端点
    this.app.get("/console-logs", (req, res) => {
      res.json(consoleLogs);
    });

    // 控制台错误端点
    this.app.get("/console-errors", (req, res) => {
      const errors = consoleLogs.filter(log => log.level === "error");
      res.json(errors);
    });

    // 网络日志端点
    this.app.get("/network-success", (req, res) => {
      res.json(networkLogs);
    });

    // 网络错误端点
    this.app.get("/network-errors", (req, res) => {
      const errors = networkLogs.filter(log => log.status >= 400);
      res.json(errors);
    });

    // 获取选中元素端点
    this.app.get("/selected-element", (req, res) => {
      res.json({ message: "Selected element endpoint" });
    });

    // 清除日志端点
    this.app.post("/wipelogs", (req, res) => {
      clearAllLogs();
      res.json({ message: "All logs cleared successfully" });
    });

    // 截图端点
    this.app.post("/capture-screenshot", async (req, res) => {
      await this.captureScreenshot(req, res);
    });

    // 审计端点
    this.setupAuditEndpoints();

    // 扩展日志端点
    this.app.post("/extension-log", (req, res) => {
      this.handleExtensionLog(req, res);
    });
  }

  /**
   * 设置审计端点
   */
  private setupAuditEndpoints(): void {
    // 可访问性审计
    this.app.post("/accessibility-audit", async (req, res) => {
      try {
        if (!currentUrl) {
          res.status(400).json({ error: "No current URL available" });
          return;
        }

        const result = await runAccessibilityAudit(currentUrl);
        res.json({
          metadata: {
            url: currentUrl,
            timestamp: new Date().toISOString(),
            category: "accessibility"
          },
          report: result
        });
      } catch (error: any) {
        console.error("Accessibility audit error:", error);
        res.status(500).json({ error: error.message });
      }
    });

    // 性能审计
    this.app.post("/performance-audit", async (req, res) => {
      try {
        if (!currentUrl) {
          res.status(400).json({ error: "No current URL available" });
          return;
        }

        const result = await runPerformanceAudit(currentUrl);
        res.json({
          metadata: {
            url: currentUrl,
            timestamp: new Date().toISOString(),
            category: "performance"
          },
          report: result
        });
      } catch (error: any) {
        console.error("Performance audit error:", error);
        res.status(500).json({ error: error.message });
      }
    });

    // SEO审计
    this.app.post("/seo-audit", async (req, res) => {
      try {
        if (!currentUrl) {
          res.status(400).json({ error: "No current URL available" });
          return;
        }

        const result = await runSEOAudit(currentUrl);
        res.json({
          metadata: {
            url: currentUrl,
            timestamp: new Date().toISOString(),
            category: "seo"
          },
          report: result
        });
      } catch (error: any) {
        console.error("SEO audit error:", error);
        res.status(500).json({ error: error.message });
      }
    });

    // 最佳实践审计
    this.app.post("/best-practices-audit", async (req, res) => {
      try {
        if (!currentUrl) {
          res.status(400).json({ error: "No current URL available" });
          return;
        }

        const result = await runBestPracticesAudit(currentUrl);
        res.json({
          metadata: {
            url: currentUrl,
            timestamp: new Date().toISOString(),
            category: "best-practices"
          },
          report: result
        });
      } catch (error: any) {
        console.error("Best practices audit error:", error);
        res.status(500).json({ error: error.message });
      }
    });
  }

  /**
   * 设置WebSocket
   */
  private setupWebSocket(): void {
    // 处理WebSocket升级请求
    this.server.on("upgrade", (request: IncomingMessage, socket: Socket, head: Buffer) => {
      if (request.url === "/extension-ws") {
        this.wss.handleUpgrade(request, socket, head, (ws: WebSocket) => {
          this.wss.emit("connection", ws, request);
        });
      }
    });

    // WebSocket连接处理
    this.wss.on("connection", (ws: WebSocket) => {
      console.error("Chrome extension connected via WebSocket");
      this.activeConnection = ws;

      ws.on("message", (message: string | Buffer | ArrayBuffer | Buffer[]) => {
        try {
          const data = JSON.parse(message.toString());
          this.handleWebSocketMessage(data);
        } catch (error) {
          console.error("Error processing WebSocket message:", error);
        }
      });

      ws.on("close", () => {
        console.error("Chrome extension disconnected");
        if (this.activeConnection === ws) {
          this.activeConnection = null;
        }
      });
    });
  }

  /**
   * 处理WebSocket消息
   */
  private handleWebSocketMessage(data: any): void {
    console.error("Received WebSocket message:", {
      ...data,
      data: data.data ? "[base64 data]" : undefined,
    });

    switch (data.type) {
      case "current-url-response":
        if (data.url) {
          console.error("Received current URL from browser:", data.url);
          currentUrl = data.url;

          if (data.tabId) {
            currentTabId = data.tabId;
          }

          if (data.requestId && this.urlRequestCallbacks.has(data.requestId)) {
            const callback = this.urlRequestCallbacks.get(data.requestId);
            if (callback) callback(data.url);
            this.urlRequestCallbacks.delete(data.requestId);
          }
        }
        break;

      case "page-navigated":
        if (data.url) {
          console.error("Page navigated to:", data.url);
          currentUrl = data.url;

          if (data.tabId) {
            currentTabId = data.tabId;
          }
        }
        break;

      case "screenshot-data":
        if (data.data) {
          console.error("Received screenshot data");
          const callbacks = Array.from(screenshotCallbacks.values());
          if (callbacks.length > 0) {
            const callback = callbacks[0];
            callback.resolve({
              data: data.data,
              path: data.path,
              autoPaste: data.autoPaste,
            });
            screenshotCallbacks.clear();
          }
        }
        break;

      case "screenshot-error":
        console.error("Received screenshot error:", data.error);
        const callbacks = Array.from(screenshotCallbacks.values());
        if (callbacks.length > 0) {
          const callback = callbacks[0];
          callback.reject(new Error(data.error || "Screenshot capture failed"));
          screenshotCallbacks.clear();
        }
        break;

      default:
        console.error("Unhandled message type:", data.type);
    }
  }

  /**
   * 处理扩展日志
   */
  private handleExtensionLog(req: express.Request, res: express.Response): void {
    console.error("\n=== Received Extension Log ===");
    const { data, settings } = req.body;

    // 更新设置
    if (settings) {
      console.error("Updating settings:", settings);
      currentSettings = { ...currentSettings, ...settings };
    }

    if (!data) {
      console.error("Warning: No data received in log request");
      res.status(400).json({ status: "error", message: "No data provided" });
      return;
    }

    console.error(`Processing ${data.type} log entry`);

    switch (data.type) {
      case "page-navigated":
        console.error("Received page navigation event with URL:", data.url);
        currentUrl = data.url;
        if (data.tabId) {
          currentTabId = data.tabId;
        }
        break;

      case "console-log":
        console.error("Adding console log:", {
          level: data.level,
          message: data.message?.substring(0, 100) + (data.message?.length > 100 ? "..." : ""),
          timestamp: data.timestamp,
        });
        consoleLogs.push(data);
        if (consoleLogs.length > currentSettings.logLimit) {
          consoleLogs.shift();
        }
        break;

      case "network-request":
        console.error("Adding network request:", {
          url: data.url,
          method: data.method,
          status: data.status,
          timestamp: data.timestamp,
        });
        networkLogs.push(data);
        if (networkLogs.length > currentSettings.logLimit) {
          networkLogs.shift();
        }
        break;

      default:
        console.error("Unknown log type:", data.type);
    }

    res.json({ status: "success" });
  }

  /**
   * 截图处理
   */
  private async captureScreenshot(req: express.Request, res: express.Response): Promise<void> {
    console.error("Browser Connector: Received request to /capture-screenshot endpoint");

    if (!this.activeConnection) {
      res.status(500).json({ error: "No active browser connection" });
      return;
    }

    try {
      // 创建promise等待截图响应
      const screenshotPromise = new Promise<any>((resolve, reject) => {
        const requestId = Date.now().toString();
        screenshotCallbacks.set(requestId, { resolve, reject });

        // 5秒超时
        setTimeout(() => {
          if (screenshotCallbacks.has(requestId)) {
            screenshotCallbacks.delete(requestId);
            reject(new Error("Screenshot request timeout"));
          }
        }, 5000);
      });

      // 发送截图请求
      this.activeConnection.send(JSON.stringify({
        type: "capture-screenshot",
        timestamp: Date.now()
      }));

      // 等待响应
      const result = await screenshotPromise;
      
      if (result.path) {
        res.json({ 
          success: true, 
          path: result.path,
          message: "Screenshot captured successfully" 
        });
      } else {
        res.status(500).json({ error: "Failed to capture screenshot" });
      }
    } catch (error: any) {
      console.error("Screenshot capture error:", error);
      res.status(500).json({ error: error.message });
    }
  }

  /**
   * 关闭连接器
   */
  async shutdown(): Promise<void> {
    console.error("Shutting down BrowserConnector...");
    
    if (this.activeConnection) {
      this.activeConnection.close();
      this.activeConnection = null;
    }

    // 关闭WebSocket服务器
    await new Promise<void>((resolve) => {
      this.wss.close(() => {
        console.error("WebSocket server closed");
        resolve();
      });
    });
  }
}

/**
 * 浏览器服务器类
 */
export class BrowserServer {
  private app: express.Application;
  private server: any;
  private browserConnector: BrowserConnector | null = null;
  private port: number;

  constructor(config: Partial<BrowserServerConfig> = {}) {
    currentSettings = { ...defaultConfig, ...config };
    this.port = currentSettings.port;

    // 创建Express应用
    this.app = express();
    this.app.use(cors());
    this.app.use(bodyParser.json({ limit: "50mb" }));
    this.app.use(bodyParser.urlencoded({ limit: "50mb", extended: true }));
  }

  /**
   * 启动服务器
   */
  async start(): Promise<number> {
    try {
      console.error(`Starting Browser Tools Server...`);
      console.error(`Requested port: ${this.port}`);

      // 查找可用端口
      this.port = await getAvailablePort(this.port);

      if (this.port !== currentSettings.port) {
        console.error(`\n====================================`);
        console.error(`NOTICE: Requested port ${currentSettings.port} was in use.`);
        console.error(`Using port ${this.port} instead.`);
        console.error(`====================================\n`);
      }

      // 创建服务器
      this.server = this.app.listen(this.port, currentSettings.host, () => {
        console.error(`\n=== Browser Tools Server Started ===`);
        console.error(`Aggregator listening on http://${currentSettings.host}:${this.port}`);

        // 记录网络接口
        const networkInterfaces = os.networkInterfaces();
        console.error("\nAvailable on the following network addresses:");

        Object.keys(networkInterfaces).forEach((interfaceName) => {
          const interfaces = networkInterfaces[interfaceName];
          if (interfaces) {
            interfaces.forEach((iface) => {
              if (!iface.internal && iface.family === "IPv4") {
                console.error(`  - http://${iface.address}:${this.port}`);
              }
            });
          }
        });

        console.error(`\nFor local access use: http://localhost:${this.port}`);
      });

      // 处理服务器错误
      this.server.on("error", (err: any) => {
        if (err.code === "EADDRINUSE") {
          console.error(`ERROR: Port ${this.port} is still in use, despite our checks!`);
        } else {
          console.error(`Server error:`, err);
        }
        throw err;
      });

      // 初始化浏览器连接器
      this.browserConnector = new BrowserConnector(this.app, this.server);

      return this.port;
    } catch (error) {
      console.error("Failed to start server:", error);
      throw error;
    }
  }

  /**
   * 停止服务器
   */
  async stop(): Promise<void> {
    console.error("Starting graceful shutdown...");

    try {
      // 关闭浏览器连接器
      if (this.browserConnector) {
        await this.browserConnector.shutdown();
      }

      // 关闭HTTP服务器
      if (this.server) {
        await new Promise<void>((resolve, reject) => {
          this.server.close((err: any) => {
            if (err) {
              console.error("Error closing HTTP server:", err);
              reject(err);
            } else {
              console.error("HTTP server closed successfully");
              resolve();
            }
          });
        });
      }

      // 清除所有日志
      clearAllLogs();

      console.error("Shutdown completed successfully");
    } catch (error) {
      console.error("Error during shutdown:", error);
      throw error;
    }
  }

  /**
   * 获取当前端口
   */
  getPort(): number {
    return this.port;
  }

  /**
   * 获取当前主机
   */
  getHost(): string {
    return currentSettings.host;
  }
} 